﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Resources;
using System.Threading.Tasks;
using Loki.Utils;
using Luna.Services.Budget.Resources;

namespace Luna.Model.Budget
{
    internal class EditableBudget : Budget, IEditableBudget
    {
        public EditableBudget()
            : base()
        {
            Data.MainBudgetID = Guid.NewGuid();
            Name = "Nouveau budget";
            Start = DateTime.Today;
            End = DateTime.Today.AddYears(1);
            Lines = new Dictionary<Guid, IEditableLine>();

            AddDefaultLines();
        }

        public EditableBudget(Budget P_Base)
            : base(P_Base)
        {
            BeginInit();

            Name = P_Base.Name;
            Comment = P_Base.Comment;
            Start = P_Base.Start;
            End = P_Base.End;
            Data.MainBudgetID = P_Base.MainBudgetID;
            LastVersion = P_Base.LastVersion;
            Validated = P_Base.Validated;

            Lines = new Dictionary<Guid, IEditableLine>();

            // copy
            foreach (var L_Line in P_Base.Lines.OfType<Line>())
            {
                var L_NewLine = new EditableLine(this, L_Line);
                Add(L_NewLine);
            }

            EndInit(!IsChanged);
        }

        IReadOnlyDictionary<Guid, IEditableLine> IEditableBudget.Lines { get { return new ReadOnlyDictionary<Guid, IEditableLine>(this.Lines); } }

        protected Dictionary<Guid, IEditableLine> Lines { get; private set; }

        #region Comment

        private static PropertyChangedEventArgs _CommentChangedArgs = ObservableHelper.CreateChangedArgs<EditableBudget>(x => x.Comment);

        private static PropertyChangingEventArgs _CommentChangingArgs = ObservableHelper.CreateChangingArgs<EditableBudget>(x => x.Comment);

        public new string Comment
        {
            get
            {
                return Data.Comment;
            }

            set
            {
                if (value != Data.Comment)
                {
                    NotifyChanging(_CommentChangingArgs);
                    Data.Comment = value;
                    NotifyChangedAndDirty(_CommentChangedArgs);
                }
            }
        }

        #endregion Comment

        #region End

        private static PropertyChangedEventArgs _EndChangedArgs = ObservableHelper.CreateChangedArgs<EditableBudget>(x => x.End);

        private static PropertyChangingEventArgs _EndChangingArgs = ObservableHelper.CreateChangingArgs<EditableBudget>(x => x.End);

        public new DateTime? End
        {
            get
            {
                return Data.End;
            }

            set
            {
                if (value != Data.End)
                {
                    NotifyChanging(_EndChangingArgs);
                    Data.End = value;
                    NotifyChangedAndDirty(_EndChangedArgs);
                }
            }
        }

        #endregion End

        #region LastVersion

        private static PropertyChangedEventArgs _LastVersionChangedArgs = ObservableHelper.CreateChangedArgs<EditableBudget>(x => x.LastVersion);

        private static PropertyChangingEventArgs _LastVersionChangingArgs = ObservableHelper.CreateChangingArgs<EditableBudget>(x => x.LastVersion);

        public new bool LastVersion
        {
            get
            {
                return Data.LastVersion;
            }

            set
            {
                if (value != Data.LastVersion)
                {
                    NotifyChanging(_LastVersionChangingArgs);
                    Data.LastVersion = value;
                    NotifyChangedAndDirty(_LastVersionChangedArgs);
                }
            }
        }

        #endregion LastVersion

        #region Name

        private static PropertyChangedEventArgs _NameChangedArgs = ObservableHelper.CreateChangedArgs<EditableBudget>(x => x.Name);

        private static PropertyChangingEventArgs _NameChangingArgs = ObservableHelper.CreateChangingArgs<EditableBudget>(x => x.Name);

        public new string Name
        {
            get
            {
                return Data.Name;
            }

            set
            {
                if (value != Data.Name)
                {
                    NotifyChanging(_NameChangingArgs);
                    Data.Name = value;
                    NotifyChangedAndDirty(_NameChangedArgs);
                }
            }
        }

        #endregion Name

        #region Start

        private static PropertyChangedEventArgs _StartChangedArgs = ObservableHelper.CreateChangedArgs<EditableBudget>(x => x.Start);

        private static PropertyChangingEventArgs _StartChangingArgs = ObservableHelper.CreateChangingArgs<EditableBudget>(x => x.Start);

        public new DateTime Start
        {
            get
            {
                return Data.Start;
            }

            set
            {
                if (value != Data.Start)
                {
                    NotifyChanging(_StartChangingArgs);
                    Data.Start = value;
                    NotifyChangedAndDirty(_StartChangedArgs);
                }
            }
        }

        #endregion Start

        #region Validated

        private static PropertyChangedEventArgs _ValidatedChangedArgs = ObservableHelper.CreateChangedArgs<EditableBudget>(x => x.Validated);

        private static PropertyChangingEventArgs _ValidatedChangingArgs = ObservableHelper.CreateChangingArgs<EditableBudget>(x => x.Validated);

        public new bool Validated
        {
            get
            {
                return Data.Validated;
            }

            set
            {
                if (value != Data.Validated)
                {
                    NotifyChanging(_ValidatedChangingArgs);
                    Data.Validated = value;
                    NotifyChangedAndDirty(_ValidatedChangedArgs);
                }
            }
        }

        #endregion Validated

        public void Add(IEditableLine P_Line)
        {
            EditableLine L_Line = P_Line as EditableLine;
            if (L_Line != null)
            {
                Lines[L_Line.ID] = L_Line;
                TrackChanges(L_Line);
            }
        }

        public void Remove(IEditableLine P_Line)
        {
            EditableLine L_Line = P_Line as EditableLine;
            if (L_Line != null)
            {
                ToolKit.Event.UnregisterStateChanged(L_Line, this);

                if (Lines.ContainsKey(L_Line.ID))
                {
                    Lines.Remove(L_Line.ID);
                }
            }
        }

        private void AddDefaultLines()
        {
            ResourceSet L_Resource = Nomenclature.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true);
            List<EditableLine> L_Lines = new List<EditableLine>();
            int L_MaxLength = 0;
            foreach (DictionaryEntry L_Item in L_Resource)
            {
                var L_NewLine = new EditableLine(this);
                L_NewLine.Code = L_Item.Key.ToString().Replace("N", "");
                L_NewLine.Name = L_Item.Value.ToString();
                L_Lines.Add(L_NewLine);
                L_MaxLength = Math.Max(L_MaxLength, L_NewLine.Code.Length);
            }

            Parallel.ForEach(L_Lines, a => a.Parent = FindParent(L_Lines, a.Code));

            foreach (var L_Line in L_Lines)
            {
                Add(L_Line);
            }
        }

        private EditableLine FindParent(List<EditableLine> P_Lines, string P_Key)
        {
            if (P_Key.Length == 1)
            {
                return null;
            }
            else
            {
                EditableLine L_Parent = null;
                string buffer = P_Key.Substring(0, P_Key.Length - 1);
                while (L_Parent == null && buffer.Length > 1)
                {
                    L_Parent = P_Lines.FirstOrDefault(x => x.Code == buffer);
                    buffer = buffer.Substring(0, buffer.Length - 1); ;
                }

                return L_Parent;
            }
        }
    }
}